package com.yanqu.road.dao.db;

import com.alibaba.druid.util.JdbcUtils;
import com.yanqu.road.dao.impl.channelcenter.ChannelServerInfoDaoImpl;
import com.yanqu.road.dao.impl.servercenter.CrossServerInfoDaoImpl;
import com.yanqu.road.dao.impl.servercenter.DbInfoDaoImpl;
import com.yanqu.road.dao.impl.servercenter.ProcessInfoDaoImpl;
import com.yanqu.road.entity.servercenter.ChannelServerInfo;
import com.yanqu.road.entity.servercenter.CrossServerInfo;
import com.yanqu.road.entity.servercenter.DbInfo;
import com.yanqu.road.entity.servercenter.ProcessInfo;
import com.yanqu.road.utils.ConfigHelper;
import com.yanqu.road.utils.CrossServerType;
import com.yanqu.road.utils.ServerType;
import com.yanqu.road.utils.net.ServerNetConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.*;
import java.util.*;

public class DBManager {
    private static final Logger logger = LogManager.getLogger(DBManager.class.getName());
    private Connection myConn;
    private static DBPoolManager pools;

    private String connName;

    private DbMonitor dbMonitor;

    private static String serverDbKey = "db_road_servercenter.url";
    private static String userDbKey = "db_road_user.url";
    private static String logDbKey = "db_road_log.url";
    private static String configDbKey = "db_road_config.url";
    private static String crossDbKey = "db_road_cross.url";
    private static String cross2DbKey = "db_road_cross2.url";
    private static String channelserverDbKey = "db_road_channelserver.url";

    public DBManager() {
    }

    public static boolean initCrossDbConfig(int serverType) {
        if (serverType == ServerType.SERVER_CROSS || serverType == ServerType.SERVER_CROSS_2 || serverType == ServerType.CENTER_CROSS_2 || serverType == ServerType.BANQUET_CROSS) {
            //跨服
            pools = DBPoolManager.getInstance("");
            if (pools == null) {
                return false;
            }
            int channelId = ConfigHelper.getInt("channelId");
            int serverId = ConfigHelper.getInt("serverId");
            CrossServerInfo crossServerInfo = new CrossServerInfoDaoImpl().getCrossServerInfo(channelId, serverId);
            List<DbInfo> infos = crossServerInfo.getDbInfos();
            if (addServerDbPool(serverType, infos)) {
                return true;
            }
            return false;
        }
        return false;
    }

    public static boolean initChannelDbConfig(int serverType) {
        if (serverType == ServerType.SERVER_CHANNEL) {
            pools = DBPoolManager.getInstance("");
            if (pools == null) {
                return false;
            }
            int serverId = ConfigHelper.getInt("serverId");
            ChannelServerInfo crossServerInfo = new ChannelServerInfoDaoImpl().getChannelServerInfo(serverId);
            List<DbInfo> infos = crossServerInfo.getDbInfos();
            if (addServerDbPool(serverType, infos)) {
                return true;
            }
            return false;
        }
        return false;
    }

    private static boolean addServerDbPool(int serverType, List<DbInfo> infos) {
        if (infos == null || infos.size() == 0) {
            return false;
        }

        Properties properties = new Properties();

        for (DbInfo info : infos) {

            String dbInfoValueHead = "jdbc:mysql://" + info.getIp() + ":" + info.getPort() + "/";
            String dbInfoValueTail = "?useUnicode=true&characterEncoding=utf8&useSSL=false|" + info.getUserName() + "|"
                    + info.getPassword() + "|" + info.getConnectionNum();

            if (info.getDbName().contains("_user") && (serverType == ServerType.SERVER_GAME || serverType == ServerType.SERVER_WEB)) {
                properties.setProperty(userDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            } else if (info.getDbName().contains("_log") && (serverType == ServerType.SERVER_GAME || serverType == ServerType.SERVER_WEB)) {
                properties.setProperty(logDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            } else if (info.getDbName().contains("_config")) {
                properties.setProperty(configDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            } else if((info.getDbName().contains("_crosscenter2") || info.getDbName().contains("_crosscenter")) && serverType == ServerType.CENTER_CROSS_2  ){
                properties.setProperty(crossDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            } else if (info.getDbName().contains("_crosscenter") && serverType == ServerType.SERVER_CROSS) {
                properties.setProperty(crossDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            } else if ((info.getDbName().contains("_cross2center") || (info.getDbName().contains("_crosscenter"))) && (serverType == ServerType.SERVER_CROSS_2 || serverType == ServerType.BANQUET_CROSS)) {
                properties.setProperty(crossDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            } else if (info.getDbName().contains("_channelserver") && serverType == ServerType.SERVER_CHANNEL) {
                properties.setProperty(channelserverDbKey, dbInfoValueHead + info.getDbName() + dbInfoValueTail);
            }
        }

        pools.addPools(properties);
        return true;
    }

    public void setConnName(String connName) {
        this.connName = connName;
    }

    public static boolean initConfig(String dbPath) {
        pools = DBPoolManager.getInstance(dbPath);
        return pools != null;
    }

    public static boolean initGameDbConfig(int serverType) {
        if (serverType == ServerType.SERVER_CROSS || serverType == ServerType.SERVER_CROSS_2 || serverType == ServerType.CENTER_CROSS_2 || serverType == ServerType.BANQUET_CROSS) {
            return initCrossDbConfig(serverType);
        } else if (serverType == ServerType.SERVER_CHANNEL) {
            return initChannelDbConfig(serverType);
        }
        pools = DBPoolManager.getInstance("");

        if (pools == null) {
            return false;
        }

        DbInfoDaoImpl daoImpl = new DbInfoDaoImpl();
        List<DbInfo> infos = daoImpl.getServerDbInfo(ConfigHelper.getInt("serverId"));
        if (addServerDbPool(serverType, infos)) {
            return true;
        }
        return false;
    }

    public static Map<String, List<ServerNetConfig>> getNetConfigFromDb() {
        ProcessInfoDaoImpl daoImpl = new ProcessInfoDaoImpl();
        List<ProcessInfo> processInfos = daoImpl.getServerProcessInfo(ConfigHelper.getInt("serverId"));
        Map<String, List<ServerNetConfig>> configs = new HashMap<>();

        for (int i = 0; i < processInfos.size(); i++) {
            ProcessInfo info = processInfos.get(i);
            ServerNetConfig configXml = new ServerNetConfig(info.getServerIndex(), info.getName(), info.getAddress(), info.getPort(), info.getAdminPort());

            if (!configs.containsKey(info.getServerName())) {
                configs.put(info.getServerName(), new ArrayList<>());
            }

            configs.get(info.getServerName()).add(configXml);
        }

        return configs;

    }

    /**
     * 获取跨服网络配置
     *
     * @return
     * @param serverType
     */
    public static Map<String, List<ServerNetConfig>> getCrossNetConfigFromDb(int serverType) {
        int channelId = ConfigHelper.getInt("channelId");
        int serverId = ConfigHelper.getInt("serverId");
        CrossServerInfo crossServerInfo = new CrossServerInfoDaoImpl().getCrossServerInfo(channelId, serverId);
        if (serverType == ServerType.SERVER_CROSS && crossServerInfo.getCrossServerType() != CrossServerType.RANK_CROSS_1) {
            throw new RuntimeException("不是中心跨服1配置");
        }
        if (serverType == ServerType.SERVER_CROSS_2 && crossServerInfo.getCrossServerType() != CrossServerType.ACTIVITY_CROSS) {
            throw new RuntimeException("不是活动跨服配置");
        }
        if (serverType == ServerType.CENTER_CROSS_2 && crossServerInfo.getCrossServerType() != CrossServerType.RANK_CROSS_2) {
            throw new RuntimeException("不是中心跨服2配置");
        }

        if (serverType == ServerType.BANQUET_CROSS && crossServerInfo.getCrossServerType() != CrossServerType.BANQUET_CROSS) {
            throw new RuntimeException("不是宴会跨服配置");
        }


        List<ProcessInfo> processInfos = crossServerInfo.getProcessInfoList();
        Map<String, List<ServerNetConfig>> configs = new HashMap<>();

        for (int i = 0; i < processInfos.size(); i++) {
            ProcessInfo info = processInfos.get(i);
            ServerNetConfig configXml = new ServerNetConfig(info.getServerIndex(), info.getName(), info.getAddress(), info.getPort(), info.getAdminPort());

            if (!configs.containsKey(info.getServerName())) {
                configs.put(info.getServerName(), new ArrayList<>());
            }

            configs.get(info.getServerName()).add(configXml);
        }

        return configs;

    }

    public static Map<String, List<ServerNetConfig>> getChannelNetConfigFromDb() {
        int serverId = ConfigHelper.getInt("serverId");
        ChannelServerInfo channelServerInfo = new ChannelServerInfoDaoImpl().getChannelServerInfo(serverId);
        List<ProcessInfo> processInfos = channelServerInfo.getProcessInfoList();

        Map<String, List<ServerNetConfig>> configs = new HashMap<>();
        for (int i = 0; i < processInfos.size(); i++) {
            ProcessInfo info = processInfos.get(i);

            ServerNetConfig configXml = new ServerNetConfig(info.getServerIndex(), info.getName(), info.getAddress(), info.getPort(), info.getAdminPort());

            if (!configs.containsKey(info.getServerName())) {
                configs.put(info.getServerName(), new ArrayList<>());
            }
            configs.get(info.getServerName()).add(configXml);
        }
        return configs;
    }


    /**
     * 释放数据库相关资源
     */
    public static void stop() {
        pools.closeFallow();
        pools = null;
    }

    private boolean openConnection() {
        try {
            if ((connName == null) || (connName.equals(""))) {
                setConnName(DbNameStrategy.getUserDb());
            }

            // return false;
            if (myConn != null && !myConn.isClosed()) {
                throw new RuntimeException("repeat Open Connection!");
            }

            dbMonitor = new DbMonitor();
            dbMonitor.openConnection();
            myConn = pools.getConnection(connName);
            if (myConn != null) {
                return true;
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);

            JdbcUtils.close(myConn);
            myConn = null;
            dbMonitor.closeConnection();
        }

        return false;

    }

    public int executeNoneQuery(String sqlText) {
        return executeNoneQuery(sqlText, null);
    }

    public int executeNoneQuery(String sqlText, Map<Integer, DbParameter> params) {
        // DbWatch watch = new DbWatch();
        int result = -1;
        if (!openConnection())
            return result;
        PreparedStatement pstmt = null;
        try {
            pstmt = myConn.prepareStatement(sqlText);
            prepareCommand(pstmt, params);

            return pstmt.executeUpdate();
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        } finally {
            closeConnection(pstmt);

        }
        return -1;

    }

    public PreparedStatement prepareStatement(String sqlText) {
        return prepareStatement(sqlText, null);
    }

    public PreparedStatement prepareStatement(String sqlText, Map<Integer, DbParameter> params) {
        // DbWatch watch = new DbWatch();
        if (!openConnection())
            return null;

        PreparedStatement pstmt = null;
        try {
            pstmt = myConn.prepareStatement(sqlText);
            prepareCommand(pstmt, params);
            return pstmt;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        // finally
        // {
        // watch.commit(sqlText);
        // }
        return null;
    }

    private void prepareCommand(PreparedStatement pstmt, Map<Integer, DbParameter> parms) throws SQLException {
        if (parms == null)
            return;
        for (Map.Entry<Integer, DbParameter> entry : parms.entrySet()) {
            pstmt.setObject(entry.getKey(), entry.getValue().getResult());
        }
    }

    public void closeConnection(PreparedStatement pstmt) {
        try {
            if (pstmt != null) {
                dbMonitor.execComplete(pstmt.toString());
            }

            if (pstmt != null && pstmt.isClosed() == false) {

                pstmt.clearParameters();
            }
            if (myConn.isClosed() == false) {

                if (myConn.getAutoCommit() != true) {
                    myConn.commit();
                    myConn.setAutoCommit(true);
                }
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } finally {
            JdbcUtils.close(pstmt);

            JdbcUtils.close(myConn);
            myConn = null;

            recordCloseConnection();
        }
    }

    public void closeConnection(Statement statement) {
        try {
            if (statement != null) {
                dbMonitor.execComplete(statement.toString());
            }

            if (myConn.isClosed() == false) {

                if (myConn.getAutoCommit() != true) {
                    myConn.commit();
                    myConn.setAutoCommit(true);
                }
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } finally {
            JdbcUtils.close(statement);

            JdbcUtils.close(myConn);
            myConn = null;
            recordCloseConnection();
        }
    }

    public void recordCloseConnection() {
        dbMonitor.closeConnection();
    }

    public void closeConnection(PreparedStatement pstmt, ResultSet rs) {
        JdbcUtils.close(rs);
        closeConnection(pstmt);
    }


    /**
     * 提供Connection接口，用于特殊处理（如事务处理）； 调用该接口后请使用closeConnection关闭连接；
     */

    public Connection getConnection() {
        if (!openConnection())
            return null;
        return myConn;
    }

    /**
     * 批处理操作
     *
     * @param sqlText
     * @param params
     * @return
     */
    public int executeBatchQuery(String sqlText, List<Map<Integer, DbParameter>> params) {
        // DbWatch watch = new DbWatch();
        if (params == null || !openConnection())
            return -1;
        int count = 0;

        PreparedStatement pstmt = null;

        try {
            myConn.setAutoCommit(false);
            pstmt = myConn.prepareStatement(sqlText, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            for (Map<Integer, DbParameter> map : params) {
                prepareCommand(pstmt, map);
                pstmt.addBatch();
                count++;
                if (count % 1500 == 0) {
                    pstmt.executeBatch();
                    myConn.commit();
                    count = 0;
                }
            }

            if (count > 0) {
                pstmt.executeBatch();
                myConn.commit();
            }

            return 1;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        } finally {

            try {
                myConn.setAutoCommit(true);
            } catch (SQLException e) {
                logger.error(e.getMessage(), e);
            }
            closeConnection(pstmt);

            // watch.commit(sqlText);
        }
        return -1;
    }


    public List<Map<String, Object>> executeQuery(String sqlText, Map<Integer, DbParameter> params) {
        // DbWatch watch = new DbWatch();
        if (!openConnection())
            return Collections.emptyList();
        PreparedStatement pstmt = null;
        try {
            pstmt = myConn.prepareStatement(sqlText);
            prepareCommand(pstmt, params);
            List<Map<String, Object>> list = new ArrayList<>();
            ResultSet resultSet = pstmt.executeQuery();
            while (resultSet.next()){
                int columnCount = resultSet.getMetaData().getColumnCount();
                Map<String, Object> map = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = resultSet.getMetaData().getColumnName(i);
                    map.put(columnName, resultSet.getObject(i));
                }
                list.add(map);
            }
            return list;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        } finally {
            closeConnection(pstmt);

        }
        return Collections.emptyList();

    }
}
